<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>函数的编写</title>

 </head>
 <body><div class="manualnavbar" style="text-align: center;">
 <div class="prev" style="text-align: left; float: left;"><a href="internals2.variables.creating.html">创建变量并设置值</a></div>
 <div class="next" style="text-align: right; float: right;"><a href="internals2.objects.html">类和对象的使用</a></div>
 <div class="up"><a href="internals2.html">PHP 核心：骇客指南</a></div>
 <div class="home"><a href="index.html">PHP Manual</a></div>
</div><hr /><div id="internals2.funcs" class="chapter">
  <h1>函数的编写</h1>

  <p class="para">
   输出到 PHP 用户层的函数是扩展的一个核心元素。甚至在打算编写面向对象扩展时，也建议阅读此章节，其中大部分信息对于编写方法也是有效的。
  </p>

  <p class="para">
   给一个扩展增加函数时，例如在使用 <a href="internals2.buildsys.skeleton.html" class="link">ext_skel</a>
   脚本创建初始结构后，可创建一个函数由 C
   函数来实现，然后在扩展的函数表中提供一个入口。函数入口可含有一个对参数信息结构的指针。
   不是必须要提供此信息，除非打算接受参数引用或返回一个引用，及提供由 PHP 的<a href="book.reflection.html" class="link">反射 API</a> 访问的信息。在下面例子中，参数不是作为直接函数参数传递的，而是通过一个堆栈(stack)，由函数实现——不能直接作为信息源——进行核对。
  </p>

  <div class="example" id="internals2.funcs.index.minimalext">
   <p><strong>Example #1 最小的仅有一个函数的 PHP 扩展</strong></p>
   <div class="example-contents">
<div class="ccode"><pre class="ccode">/* {{{ proto void hello_world()
       Do nothing */
PHP_FUNCTION(hello_world)
{
}
/* }}} */

/* {{{ arginfo_hello_world */
ZEND_BEGIN_ARG_INFO(arginfo_hello_world, 0)
ZEND_END_ARG_INFO()
/* }}} */

/* {{{ demo_functions */
function_entry demo_functions[] = {
    PHP_FE(hello_world, arginfo_hello_world)
    {NULL, NULL, NULL}
}
/* }}} */

/* {{{ demo_module_enry */
zend_module_entry demo_module_entry = {
#if ZEND_MODULE_API_NO &gt;= 20010901
    STANDARD_MODULE_HEADER,
#endif
    &quot;demo&quot;,
    demo_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO &gt;= 20010901
    &quot;1.0.0&quot;,
#en
    STANDARD_MODULE_PROPERTIES
}
/* }}} */</pre>
</div>
   </div>

  </div>

  <p class="para">
   在此例程中，可以你看到上面讨论的元素和模块结构。模块结构可参见
   <a href="internals2.structure.modstruct.html" class="xref">The zend_module structure</a>。
  </p>

  <p class="para">
   此扩展的第一部分是实际的实现内容。
   按照惯例，在每一个输出的函数之前用两行注释及函数后的一行短注释来描述函数的用户层模型。
  </p>

  <p class="para">
   为了源代码对不同版本 PHP 提供兼容性，函数声明被包装在 <code class="code">PHP_FUNCTION</code>
   宏里。编译器的预处理器会使用 PHP 5.3 将此转换为如下函数：
  </p>

  <div class="example-contents screen">
<div class="cdata"><pre>
void zif_hello_world(int ht, zval *return_value, zval **return_value_ptr,
                     zval *this_ptr, int return_value_used TSRMLS_DC)
{
}
</pre></div>
  </div>

  <p class="para">
   为预防输出至用户层的函数与其他的同名函数间的命名冲突，输出函数的 C
   符号用 <code class="code">zif_</code> 作为前缀。你也可看到，此模型没有引用参数堆栈。如果存取
   PHP 传递的参数，在下面进行描述。下方表格中列举了在
   <code class="code">INTERNAL_FUNCTION_PARAMETERS</code> 宏中定义了的 C
   语言级别的参数。使用所提供的宏时，请注意这些参数可能在 PHP 版本间会发生变化。
  </p>

  <table id="internals2.funcs.index.internal_func_params" class="doctable table">
   <caption><strong>INTERNAL_FUNCTION_PARAMETERS</strong></caption>
   
    <thead>
     <tr>
      <th>名称和类型</th>
      <th>描述</th>
      <th>访问宏</th>
     </tr>

    </thead>

    <tbody class="tbody">
                                                    
     <tr>
      <td><code class="code">int ht</code></td>
      <td>用户实际传递参数的数量</td>
      <td><code class="code">ZEND_NUM_ARGS()</code></td>
     </tr>


     <tr>
      <td><code class="code">zval *return_value</code></td>
      <td>
       PHP 变量的指针，可填充返回值传递给用户。默认值是 <code class="code">IS_NULL</code>。
      </td>
      <td><code class="code">RETVAL_*</code>, <code class="code">RETURN_*</code></td>
     </tr>


     <tr>
      <td><code class="code">zval **return_value_ptr</code></td>
      <td>
       当返回引用时，PHP 将其设为变量的指针。不建议返回引用。
      </td>
      <td class="empty">&nbsp;</td>
     </tr>


     <tr>
      <td><code class="code">zval *this_ptr</code></td>
      <td>
       假如这是一个方法调用，其指向存放 <code class="code">$this</code> 对象的 PHP 变量。
      </td>
      <td><code class="code">getThis()</code></td>
     </tr>


     <tr>
      <td><code class="code">int return_value_used</code></td>
      <td>
       指示返回值是否会被调用者使用的标志。
       caller.
      </td>
      <td class="empty">&nbsp;</td>
     </tr>


    </tbody>
   
  </table>


  <p class="para">
   上面所述的函数什么事都不做，只是简单地返回 NULL 给用户。从 PHP
   用户层可以带任意数量的参数调用此函数。更有用的函数通常由四部分组成：
  </p>

  <ol type="1">
   <li class="listitem">
    <p class="para">
     局部变量定义。在 C 语言中必须在函数的开头定义局部变量。
    </p>
   </li>
   <li class="listitem">
    <p class="para">
     解析的参数。PHP 传递参数到一个特殊的堆栈上，从那里对参数进行读取、校验类型，有问题时根据需要可进行转换。
    </p>
   </li>
   <li class="listitem">
    <p class="para">
     实际逻辑。做所需做的。
    </p>
   </li>
   <li class="listitem">
    <p class="para">
     设定返回值，清理，返回。
    </p>
   </li>
  </ol>

  <p class="para">
   在某些情况下，这些步骤的实际顺序可能发生变化。尤其是最后两步经常颠倒，但最好保持那个顺序。
  </p>

  <div class="example" id="internals2.funcs.index.simplefunc">
   <p><strong>Example #2 一个简单的函数</strong></p>
   <div class="example-contents">
<div class="ccode"><pre class="ccode">/* {{{ proto void hello_world(string name)
   Greets a user */
PHP_FUNCTION(hello_world)
{
    char *name;
    int name_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &quot;s&quot;, &amp;name, &amp;name_len) == FAILURE) {
        return;
    }

    php_printf(&quot;Hello %s!&quot;, name);

    RETURN_TRUE;
}
/* }}} */</pre>
</div>
   </div>

  </div>

  <p class="para">
   上面的函数展示了那些部分。让我们开始分析最后几行：<code class="code">php_printf</code>
   可以很容易的被猜到是标准的 C 语言的 <code class="code">printf</code> 针对 PHP
   的版本。与 <code class="code">printf</code> 不同，它不是打印到进程的 <code class="code">STDOUT</code>
   通道，而是到当前的输出流，可被用户进行缓冲。要注意的是，<code class="code">php_printf</code>
   不象大部分 PHP 的 API，它不是二进制安全的。想要二进制安全输出应使用
   <code class="code">PHPWRITE</code>。
  </p>

  <blockquote class="note"><p><strong class="note">Note</strong>: 
   <span class="simpara">
    通常来说，要直接向输出流打印数据，建议改为返回字符串数据给用户，由用户决定做什么。
    此规则的例外情况可能是处理二进制数据（如图像）的函数，API
    要求就在函数里提供方法来存取数据。
   </span>
  </p></blockquote>

  <p class="para">
   最后一行的 <code class="code">RETURN_TRUE</code> 宏做了三件事：将
   <code class="code">return_value</code> 指针变量的类型设为
   <code class="code">IS_BOOLEAN</code>，并将其值设为 <code class="code">true</code> 值。然后从 C
   函数中返回。所以当使用此宏时，你就是内存和要结束的其他资源的管家，此函数不会执行其后的代码。
  </p>

  <p class="para">
   <code class="code">zend_parse_parameters()</code>
   函数负责读取用户从参数堆栈传递来参数，并将其适当地转换后放入局部 C
   语言变量。如果用户传递的参数个数有误或类型不可被转换，
   函数会发出一个冗长的错误信息，并返回 <code class="code">FAILURE</code>。
   当从函数简单返回的情况下——不修改 <code class="code">return_value</code>，将默认的返回值
   <code class="code">NULL</code> 返回给用户。
  </p>

  <blockquote class="note"><p><strong class="note">Note</strong>: 
   <span class="simpara">
    请注意，<code class="code">FAILURE</code> 表示为 <code class="code">-1</code>，<code class="code">SUCCESS</code>
    表示为 <code class="code">0</code>。为了使代码清晰，应总是使用已命名的常量而不是其值。
   </span>
  </p></blockquote>

  <p class="para">
   传递给 <code class="code">zend_parse_parameters()</code>
   的第一个参数是用户实际传递到函数的参数数量。此数值做为 <code class="code">ht</code>
   参数传给函数，但就像上面讨论的那样，应使用做为实现细节的
   <code class="code">ZEND_NUM_ARGS()</code>。
  </p>

  <p class="para">
   为了与 PHP 的线程隔离、线程安全资源管理器兼容，还要用 <code class="code">TSRMLS_CC</code>
   传递线程上下文。与其他函数不同，它不能是最后的参数，因为在
   <code class="code">zend_parse_parameters</code>
   内要求有不定数量的参数——依赖于要读取的用户参数的数量。
  </p>

  <p class="para">
   在线程上下文之后定义所要求的参数。每个参数都由字符串中的一个字符表示其类型。
   如果希望一个字符串参数，则在此类型说明只不过是个 <code class="code">&quot;s&quot;</code>。
  </p>

  <p class="para">
   最后一个是传递一个或多个指针给要填充变量值的 C
   变量，或提供更多细节。比如字符串，事实上的字符串，总是以 NULL
   结尾，以 <code class="code">char*</code>，且其长度是除 NULL 字节外的 <code class="code">int</code> 型值。
  </p>

  <p class="para">
   相关所有类型说明符和对应的附加的 C 语言类型的文档可在源代码发布包中的文件
   <code class="code">README.PARAMETER_PARSING_API</code>
   中找到。大多数重要类型可见下表。
  </p>

  <table id="internals2.funcs.index.zend_pars_params_types" class="doctable table">
   <caption><strong>zend_parse_parameters() 类型说明符</strong></caption>
   
    <thead>
     <tr>
      <th>修饰符</th>
      <th>附加参数的类型</th>
      <th>描述</th>
     </tr>

    </thead>

    <tbody class="tbody">

     <tr>
      <td><code class="code">b</code></td>
      <td><code class="code">zend_bool</code></td>
      <td>Boolean 值</td>
     </tr>


     <tr>
      <td><code class="code">l</code></td>
      <td><code class="code">long</code></td>
      <td>integer (long) 值</td>
     </tr>


     <tr>
      <td><code class="code">d</code></td>
      <td><code class="code">double</code></td>
      <td>float (double) 值</td>
     </tr>


     <tr>
      <td><code class="code">s</code></td>
      <td><code class="code">char*, int</code></td>
      <td>二进制的安全串</td>
     </tr>


     <tr>
      <td><code class="code">h</code></td>
      <td><code class="code">HashTable*</code></td>
      <td>数组的哈希表</td>
     </tr>

    </tbody>
   
  </table>


 </div>
<hr /><div class="manualnavbar" style="text-align: center;">
 <div class="prev" style="text-align: left; float: left;"><a href="internals2.variables.creating.html">创建变量并设置值</a></div>
 <div class="next" style="text-align: right; float: right;"><a href="internals2.objects.html">类和对象的使用</a></div>
 <div class="up"><a href="internals2.html">PHP 核心：骇客指南</a></div>
 <div class="home"><a href="index.html">PHP Manual</a></div>
</div></body></html>
